package com.zhangsr.zus.config.api;

import com.zhangsr.zus.common.Constants;
import com.zhangsr.zus.common.URL;
import com.zhangsr.zus.common.Version;
import com.zhangsr.zus.common.utils.NetUtils;
import com.zhangsr.zus.rpc.api.Exporter;
import com.zhangsr.zus.rpc.api.RpcProtocol;
import com.zhangsr.zus.rpc.api.RpcProxyFactory;
import com.zhangsr.zus.rpc.api.proxy.jdk.JdkProxyFactory;
import com.zhangsr.zus.rpc.zus.ZusRpcProtocol;
import org.apache.log4j.Logger;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: zhangsr
 */
public class ZusServiceConfig<T> extends AbstractServiceConfig {

    private static final Logger logger = Logger.getLogger(ZusServiceConfig.class);

    private static final RpcProtocol protocol = new ZusRpcProtocol();
    private static final RpcProxyFactory proxyFactory = new JdkProxyFactory();

    // 接口类型
    private String interfaceName;

    private Class<?> interfaceClass;

    // 接口实现类引用
    private T ref;

    // 延迟暴露
    protected Integer delay;

    private final List<URL> urls = new ArrayList<URL>();
    private final List<Exporter<?>> exporters = new ArrayList<Exporter<?>>();

    private transient volatile boolean exported;

    public ZusServiceConfig() {
        this.exported = false;
    }

    public synchronized void export() {
        if (delay != null && delay > 0) {
            // 延迟暴露
            Thread thread = new Thread(new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(delay);
                    } catch (Throwable e) {
                    }
                    doExport();
                }
            });
            thread.setDaemon(true);
            thread.setName("DelayExportServiceThread");
            thread.start();
        } else {
            doExport();
        }
    }

    protected synchronized void doExport() {
        if (exported) {
            return;
        }
        exported = true;
        if (interfaceName == null || interfaceName.length() == 0) {
            throw new IllegalStateException("<zus:service interface=\"\" /> interface not allow null!");
        }
        try {
            interfaceClass = Class.forName(interfaceName, true, Thread.currentThread().getContextClassLoader());
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
        doExportUrls();
    }

    private void doExportUrls() {
        for (ZusProtocolConfig protocolConfig : protocols) {
            logger.debug("export protocol = " + protocolConfig.getId());
            doExportUrlsFor1Protocol(protocolConfig);
        }
    }

    private void doExportUrlsFor1Protocol(ZusProtocolConfig protocolConfig) {
        // name
        String name = protocolConfig.getName();
        if (name == null || name.length() == 0) {
            name = "zus";
        }
        // host
        String host = protocolConfig.getHost();
        if (NetUtils.isInvalidLocalHost(host)) {
            try {
                host = InetAddress.getLocalHost().getHostAddress();
            } catch (UnknownHostException e) {
                logger.warn(e.getMessage(), e);
            }
        }
        // port
        Integer port = protocolConfig.getPort();
        final int defaultPort = protocol.getDefaultPort();
        if (port == null || port == 0) {
            port = defaultPort;
        }
        // parameters
        Map<String, String> map = new HashMap<String, String>();
        map.put(Constants.ZUS_VERSION_KEY, Version.getVersion());
        map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
        appendParameters(map, protocolConfig);

        // export service
        URL url = new URL(name, host, port, map);
        logger.info("Export zus service " + interfaceClass.getName() + " to url " + url);

        Exporter<?> exporter = protocol.export(proxyFactory.getInvoker(ref, (Class) interfaceClass, url));
        exporters.add(exporter);
        urls.add(url);
    }

    public synchronized void unexport() {
        if (logger.isInfoEnabled()) logger.info("ZUS unexport service ...");
        if (isExported())
            this.exported = false;
    }

    public void destroy() throws Exception {
        logger.info("ZUS destroy Service...");
    }

    //------------------- setter && is -------------------

    public boolean isExported() {
        return exported;
    }

    //---------------- setter &&  getter -----------------

    public String getInterface() {
        return interfaceName;
    }

    public void setInterface(String interfaceName) {
        this.interfaceName = interfaceName;
        if (id == null || id.length() == 0) {
            id = interfaceName;
        }
    }

    public void setInterface(Class<?> interfaceClass) {
        if (interfaceClass != null && !interfaceClass.isInterface()) {
            throw new IllegalStateException("The interface class " + interfaceClass + " is not a interface!");
        }
        this.interfaceClass = interfaceClass;
        setInterface(interfaceClass == null ? null : interfaceClass.getName());
    }

    public Class<?> getInterfaceClass() {
        if (interfaceClass != null) {
            return interfaceClass;
        }
        try {
            if (interfaceName != null && interfaceName.length() > 0) {
                this.interfaceClass = Class.forName(interfaceName, true, Thread.currentThread().getContextClassLoader());
            }
        } catch (ClassNotFoundException t) {
            throw new IllegalStateException(t.getMessage(), t);
        }
        return interfaceClass;
    }

    public void setInterfaceClass(Class<?> interfaceClass) {
        setInterface(interfaceClass);
    }

    public T getRef() {
        return ref;
    }

    public void setRef(T ref) {
        this.ref = ref;
    }
}
